#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#endregion

namespace KLib.Graphics
{
    [RegistersService(typeof(IMaterialService))]
    public class MaterialManager : KLib.GameComponent, IMaterialService
    {
        #region Fields
        private string rootDirectory;
        ContentManager content;
        Dictionary<string, Material> materials = new Dictionary<string, Material>();

        private Texture2D materialNotFoundTexture;
        #endregion

        #region Properties
        private string RootDirectory
        {
            get { return rootDirectory; }
            set
            { 
                rootDirectory = value;
                content.RootDirectory = "Content/" + rootDirectory;
            }
        }
        #endregion

        #region Initialization

        public MaterialManager(Game game)
            : base(game)
        {
            content = new ContentManager(game.Services, "Content");
            materialNotFoundTexture = TextureGenerator.GenerateRectangle(1, 1, Color.Red);
        }

        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        #endregion

        #region Methods
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        #region AddMaterial()
        public void AddMaterial(string name, Material material)
        {
            if (materials.ContainsKey(name))
                return;
            materials.Add(name, material);
            if (MaterialAdded != null)
                MaterialAdded(materials[name]);
        }

        public void AddMaterial(string name, Texture2D texture)
        {
            if (materials.ContainsKey(name))
                return;
            
            materials.Add(name,new Material(texture,null));
            if (MaterialAdded != null)
                MaterialAdded(materials[name]);
        }

        public void AddMaterial(string name, Effect effect)
        {
            if (materials.ContainsKey(name))
                return;
            materials.Add(name,new Material(null,effect));
            if (MaterialAdded != null)
                MaterialAdded(materials[name]);
        }
        public void AddMaterial(string name, Texture2D texture, Effect effect)
        {
            if (materials.ContainsKey(name))
                return;
            materials.Add(name,new Material(texture,effect));
            if (MaterialAdded != null)
                MaterialAdded(materials[name]);
        }
        #endregion

        #region LoadMaterial()

        /// <summary>
        /// Loads a Material that assumes the name, textureName, and effectName all are the same
        /// </summary>
        /// <param name="name">The name of the material. This will be used to retreive data from the MaterialManager</param>
        public void LoadMaterial(string name)
        {
            LoadMaterial(name, name, name);
        }

        /// <summary>
        /// Loads a Material that assumes the name and effectName all are the same, and textureName is different
        /// </summary>
        /// <param name="name">The name of the material. This will be used to retreive data from the MaterialManager</param>
        /// <param name="textureName">The Name of the Texture asset</param>
        public void LoadMaterial(string name, string textureName)
        {
            LoadMaterial(name, textureName, name);
        }
        /// <summary>
        /// Loads a Material that assumes the name, textureName, and effectName all are the same
        /// </summary>
        /// <param name="name">The name of the material. This will be used to retreive data from the MaterialManager</param>
        /// <param name="effectName">The Name of the Texture asset</param>
        /// <param name="textureName">The Name of the Effect asset</param>
        public void LoadMaterial(string name, string textureName, string effectName)
        {
            if (materials.ContainsKey(name))
                return;

            Texture2D texture;
            Effect effect;

            try
            {
                texture = content.Load<Texture2D>("Textures/" + textureName);
            }
            catch
            {
                texture = null;
            }
            try
            {
                effect = content.Load<Effect>("Effects/" + effectName);
            }
            catch
            {
                effect = null;
            }

            if (texture == null && effect == null)
                materials.Add(name, Material.Blank);
            else
                materials.Add(name, new Material(ref texture, effect));

            if (MaterialAdded != null)
                MaterialAdded(materials[name]);
        }
        #endregion

        #region LoadAndGetTexture()
        public Texture2D LoadAndGetTexture(string assetName)
        {
            LoadMaterial(assetName);
            return GetTexture(assetName);
        }
        #endregion

        #region GetTexture()
        public Texture2D GetTexture(string materialName)
        {
            if (materials.ContainsKey(materialName))
            {
                if (materials[materialName].Texture != null
                    && materials[materialName].Texture.IsDisposed == false)
                    return materials[materialName].Texture;
            }
            return materialNotFoundTexture;
        }

        public void GetTexture(string materialName, out Texture2D texture)
        {
            if (materials.ContainsKey(materialName))
            {
                if (materials[materialName].Texture != null
                    && materials[materialName].Texture.IsDisposed == false)
                    texture = materials[materialName].Texture;
                else
                    texture = materialNotFoundTexture;
            }
            else
                texture = materialNotFoundTexture;
        }
        #endregion

        #region GetMaterial()
        public Material GetMaterial(string materialName)
        {
            if (materials.ContainsKey(materialName))
                return materials[materialName];
            else 
                return Material.Blank;
        }
        #endregion

        #region RemoveMaterial()
        public void RemoveMaterial(string name)
        {
            if (materials.ContainsKey(name))
            {
                materials[name].Dispose();
                materials.Remove(name);
                if (MaterialAdded != null)
                    MaterialAdded(materials[name]);
            }
        }
        #endregion

        #region Unload()
        public void Unload()
        {
            this.content.Unload();
            this.materials.Clear();
        }
        #endregion
        #endregion

        #region Events
        public delegate void MaterialEventHandler(Material material);
        public event MaterialEventHandler MaterialAdded;
        public event MaterialEventHandler MaterialRemoved;
        #endregion

        #region Helper Methods

        #endregion
    }
}